#include "util/stdc.h"
#include "util/charset.h"
#include "sip9/message.h"
namespace sip{

struct DecodeContext
{
    const char *buf;
    int len;
    int ret;
    string err;
};

#define decode_return_fail(ctx, str)                                         \
{                                                                            \
    ctx.ret = 1;                                                             \
    ctx.err = str;                                                           \
    return;                                                                  \
}

#define decode_check_fail(ctx)                                               \
{                                                                            \
    if (ctx.ret != 0)                                                        \
        return;                                                              \
}

const string ALPHANUM = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";

inline bool is_char_wsp(char c)
{
    return (c == ' ' || c == '\t');
}

inline bool is_char_reserved(char c)
{
    static util::CharSet cset = ";/?:@&=+$,";
    return cset.is_set(c);
}

inline bool is_char_unreserved(char c)
{
    static util::CharSet cset = ALPHANUM + "-_.!~*'()";
    return cset.is_set(c);
}

inline bool is_char_mark(char c)
{
    static util::CharSet cset = "-_.!~*'()";
    return cset.is_set(c);
}

inline bool is_char_token(char c)
{
    static util::CharSet cset = ALPHANUM + "-.!%*_+`'~";
    return cset.is_set(c);
}

inline bool is_char_word(char c)
{
    static util::CharSet cset = ALPHANUM + "-.!%*-+`'~()<>:\\\"/[]?{}";
    return cset.is_set(c);
}

inline bool is_char_text(char c)
{
    return (isgraph(c) || c < 0);
}

inline bool is_char_scheme(char c)
{
    static util::CharSet cset = ALPHANUM + "+-.";
    return cset.is_set(c);
}

inline bool is_char_user(char c)
{
    static util::CharSet cset = ALPHANUM + "-_.!~*'()" + "%" + "&=+$,;?/";
    return cset.is_set(c);
}

inline bool is_char_passwd(char c)
{
    static util::CharSet cset = ALPHANUM + "-_.!~*'()" + "%" + "&=+$,";
    return cset.is_set(c);
}

inline bool is_char_uri_param(char c)
{
    static util::CharSet cset = ALPHANUM + "-_.!~*'()" + "%" + "[]/:&+$";
    return cset.is_set(c);
}

inline bool is_char_uri_header(char c)
{
    static util::CharSet cset = ALPHANUM + "-_.!~*'()" + "%" + "[]/?:+$";
    return cset.is_set(c);
}

inline bool is_char_host(char c)
{
    static util::CharSet cset = ALPHANUM + "-.:[]";
    return cset.is_set(c);
}

inline bool is_token_crlf(const char c[2])
{
    return (c[0] == '\r' && c[1] == '\n');
}

inline bool is_token_lws(const char c[3])
{
    return (c[0] == '\r' && c[1] == '\n' && (c[2] == ' ' || c[2] == '\t'));
}

inline bool is_token_escap(const char c[3])
{
    return (c[0] == '%' && isxdigit(c[1]) && isxdigit(c[2]));
}

inline void decode_token_space(DecodeContext &ctx)
{
    if (ctx.buf[0] != ' ')
        decode_return_fail(ctx, "token space err");

    ctx.buf += 1;
    ctx.len -= 1;
}

inline void decode_token_crlf(DecodeContext &ctx)
{
    if (ctx.buf[0] != '\r' || ctx.buf[1] != '\n')
        decode_return_fail(ctx, "token crlf err");

    ctx.buf += 2;
    ctx.len -= 2;
}

// LWS = [*WSP CRLF] 1*WSP
// WSP = SP / HTAB
void decode_token_lws(DecodeContext &ctx)
{
    int i = 0;

    while (is_char_wsp(ctx.buf[i]))
        i += 1;

    if (is_token_lws(&ctx.buf[i]))
    {
        i += 3;
        while (is_char_wsp(ctx.buf[i]))
            i += 1;
    }

    if (i <= 0)
        decode_return_fail(ctx, "token lws err");

    ctx.buf += i;
    ctx.len -= i;
}

// SWS = [LWS]
// LWS = [*WSP CRLF] 1*WSP
// WSP = SP / HTAB
void decode_token_sws(DecodeContext &ctx)
{
    int i = 0;

    while (is_char_wsp(ctx.buf[i]))
        i += 1;

    if (is_token_lws(&ctx.buf[i]))
    {
        i += 3;
        while (is_char_wsp(ctx.buf[i]))
            i += 1;
    }

    if (i > 0)
    {
        ctx.buf += i;
        ctx.len -= i;
    }
}

// HCOLON = *(SP / HT) ":" SWS
inline void decode_token_hcolon(DecodeContext &ctx)
{
    int i = 0;

    while (is_char_wsp(ctx.buf[i]))
        i += 1;

    ctx.buf += i;
    ctx.len -= i;

    if (ctx.buf[0] != ':')
        decode_return_fail(ctx, "token hcolon err");

    ctx.buf += 1;
    ctx.len -= 1;

    decode_token_sws(ctx);
}

// LANGLE = SWS "<"
bool verify_token_l_dquote(DecodeContext &ctx)
{
    int i = 0;

    while (is_char_wsp(ctx.buf[i]))
        i += 1;

    if (is_token_lws(&ctx.buf[i]))
    {
        i += 3;
        while (is_char_wsp(ctx.buf[i]))
            i += 1;
    }

    if (ctx.buf[i] == '"')
        return true;

    return false;
}

// LDQUOTE = SWS DQUOTE
void decode_token_l_dquote(DecodeContext &ctx)
{
    decode_token_sws(ctx);

    if (ctx.buf[0] != '"')
        decode_return_fail(ctx, "left dquote err");

    ctx.buf += 1;
    ctx.len -= 1;
}

// RDQUOTE = DQUOTE SWS
void decode_token_r_dquote(DecodeContext &ctx)
{
    if (ctx.buf[0] != '"')
        decode_return_fail(ctx, "right dquote err");

    ctx.buf += 1;
    ctx.len -= 1;

    decode_token_sws(ctx);
}

// LANGLE = SWS "<"
inline void decode_token_l_angle(DecodeContext &ctx)
{
    decode_token_sws(ctx);

    if (ctx.buf[0] != '<')
        decode_return_fail(ctx, "left angle err");

    ctx.buf += 1;
    ctx.len -= 1;
}

// RANGLE = ">" SWS
inline void decode_token_r_angle(DecodeContext &ctx)
{
    if (ctx.buf[0] != '>')
        decode_return_fail(ctx, "right angle err");

    ctx.buf += 1;
    ctx.len -= 1;

    decode_token_sws(ctx);
}

// DELIMITER = SWS "*" SWS
bool verify_token_delimiter(DecodeContext &ctx, char delimiter)
{
    int i = 0;

    while (is_char_wsp(ctx.buf[i]))
        i += 1;

    if (is_token_lws(&ctx.buf[i]))
    {
        i += 3;
        while (is_char_wsp(ctx.buf[i]))
            i += 1;
    }

    if (ctx.buf[i] == delimiter)
        return true;

    return false;
}

// DELIMITER = SWS "*" SWS
void decode_token_delimiter(DecodeContext &ctx, char delimiter)
{
    decode_token_sws(ctx);

    if (ctx.buf[0] != delimiter)
        decode_return_fail(ctx, "token delimiter err");

    ctx.buf += 1;
    ctx.len -= 1;

    decode_token_sws(ctx);
}

// Sip = "SIP"
inline void decode_token_sip(DecodeContext &ctx)
{
    if ((ctx.buf[0] == 's' || ctx.buf[0] == 'S')
     && (ctx.buf[1] == 'i' || ctx.buf[1] == 'I')
     && (ctx.buf[2] == 'p' || ctx.buf[2] == 'P'))
    {
        ctx.buf += 3;
        ctx.len -= 3;
    }
    else
    {
        decode_return_fail(ctx, "token sip err");
    }
}

// Ver = "2.0"
inline void decode_token_ver(DecodeContext &ctx)
{
    if (ctx.buf[0] == '2'
     && ctx.buf[1] == '.'
     && ctx.buf[2] == '0')
    {
        ctx.buf += 3;
        ctx.len -= 3;
    }
    else
    {
        decode_return_fail(ctx, "token ver err");
    }
}

// SipVer = "SIP/2.0"
inline void decode_token_sip_ver(DecodeContext &ctx)
{
    if ((ctx.buf[0] == 's' || ctx.buf[0] == 'S')
     && (ctx.buf[1] == 'i' || ctx.buf[1] == 'I')
     && (ctx.buf[2] == 'p' || ctx.buf[2] == 'P')
     &&  ctx.buf[3] == '/'
     &&  ctx.buf[4] == '2'
     &&  ctx.buf[5] == '.'
     &&  ctx.buf[6] == '0')
    {
        ctx.buf += 7;
        ctx.len -= 7;
    }
    else
    {
        decode_return_fail(ctx, "token sipver err");
    }
}

inline bool verify_token_invite(DecodeContext &ctx)
{
    if (ctx.buf[0] == 'I'
     && ctx.buf[1] == 'N'
     && ctx.buf[2] == 'V'
     && ctx.buf[3] == 'I'
     && ctx.buf[4] == 'T'
     && ctx.buf[5] == 'E'
     && !is_char_token(ctx.buf[6]))
    {
        return true;
    }
    else
    {
        return false;
    }
}

inline bool verify_token_ack(DecodeContext &ctx)
{
    if (ctx.buf[0] == 'A'
     && ctx.buf[1] == 'C'
     && ctx.buf[2] == 'K'
     && !is_char_token(ctx.buf[3]))
    {
        return true;
    }

    return false;
}

inline bool verify_token_bye(DecodeContext &ctx)
{
    if (ctx.buf[0] == 'B'
     && ctx.buf[1] == 'Y'
     && ctx.buf[2] == 'E'
     && !is_char_token(ctx.buf[3]))
    {
        return true;
    }

    return false;
}

inline bool verify_token_cancel(DecodeContext &ctx)
{
    if (ctx.buf[0] == 'C'
     && ctx.buf[1] == 'A'
     && ctx.buf[2] == 'N'
     && ctx.buf[3] == 'C'
     && ctx.buf[4] == 'E'
     && ctx.buf[5] == 'L'
     && !is_char_token(ctx.buf[6]))
    {
        return true;
    }

    return false;
}

inline bool verify_token_update(DecodeContext &ctx)
{
    if (ctx.buf[0] == 'U'
     && ctx.buf[1] == 'P'
     && ctx.buf[2] == 'D'
     && ctx.buf[3] == 'A'
     && ctx.buf[4] == 'T'
     && ctx.buf[5] == 'E'
     && !is_char_token(ctx.buf[6]))
    {
        return true;
    }

    return false;
}

inline bool verify_token_prack(DecodeContext &ctx)
{
    if (ctx.buf[0] == 'P'
     && ctx.buf[1] == 'R'
     && ctx.buf[2] == 'A'
     && ctx.buf[3] == 'C'
     && ctx.buf[4] == 'K'
     && !is_char_token(ctx.buf[5]))
    {
        return true;
    }

    return false;
}

inline bool verify_token_message(DecodeContext &ctx)
{
    if (ctx.buf[0] == 'M'
     && ctx.buf[1] == 'E'
     && ctx.buf[2] == 'S'
     && ctx.buf[3] == 'S'
     && ctx.buf[4] == 'A'
     && ctx.buf[5] == 'G'
     && ctx.buf[6] == 'E'
     && !is_char_token(ctx.buf[7]))
    {
        return true;
    }

    return false;
}

inline bool verify_token_info(DecodeContext &ctx)
{
    if (ctx.buf[0] == 'I'
     && ctx.buf[1] == 'N'
     && ctx.buf[2] == 'F'
     && ctx.buf[3] == 'O'
     && !is_char_token(ctx.buf[4]))
    {
        return true;
    }

    return false;
}

inline bool verify_token_register(DecodeContext &ctx)
{
    if (ctx.buf[0] == 'R'
     && ctx.buf[1] == 'E'
     && ctx.buf[2] == 'G'
     && ctx.buf[3] == 'I'
     && ctx.buf[4] == 'S'
     && ctx.buf[5] == 'T'
     && ctx.buf[6] == 'E'
     && ctx.buf[7] == 'R'
     && !is_char_token(ctx.buf[8]))
    {
        return true;
    }

    return false;
}

inline bool verify_token_options(DecodeContext &ctx)
{
    if (ctx.buf[0] == 'O'
     && ctx.buf[1] == 'P'
     && ctx.buf[2] == 'T'
     && ctx.buf[3] == 'I'
     && ctx.buf[4] == 'O'
     && ctx.buf[5] == 'N'
     && ctx.buf[6] == 'S'
     && !is_char_token(ctx.buf[7]))
    {
        return true;
    }

    return false;
}

inline bool verify_token_subscribe(DecodeContext &ctx)
{
    if (ctx.buf[0] == 'S'
     && ctx.buf[1] == 'U'
     && ctx.buf[2] == 'B'
     && ctx.buf[3] == 'S'
     && ctx.buf[4] == 'C'
     && ctx.buf[5] == 'R'
     && ctx.buf[6] == 'I'
     && ctx.buf[7] == 'B'
     && ctx.buf[8] == 'E'
     && !is_char_token(ctx.buf[9]))
    {
        return true;
    }

    return false;
}

inline bool verify_token_notify(DecodeContext &ctx)
{
    if (ctx.buf[0] == 'N'
     && ctx.buf[1] == 'O'
     && ctx.buf[2] == 'T'
     && ctx.buf[3] == 'I'
     && ctx.buf[4] == 'F'
     && ctx.buf[5] == 'Y'
     && !is_char_token(ctx.buf[6]))
    {
        return true;
    }

    return false;
}

inline bool verify_token_refer(DecodeContext &ctx)
{
    if (ctx.buf[0] == 'R'
     && ctx.buf[1] == 'E'
     && ctx.buf[2] == 'F'
     && ctx.buf[3] == 'E'
     && ctx.buf[4] == 'R'
     && !is_char_token(ctx.buf[5]))
    {
        return true;
    }

    return false;
}

inline bool verify_token_publish(DecodeContext &ctx)
{
    if (ctx.buf[0] == 'P'
     && ctx.buf[1] == 'U'
     && ctx.buf[2] == 'B'
     && ctx.buf[3] == 'L'
     && ctx.buf[4] == 'I'
     && ctx.buf[5] == 'S'
     && ctx.buf[6] == 'H'
     && !is_char_token(ctx.buf[7]))
    {
        return true;
    }

    return false;
}

inline bool verify_token_cseq(DecodeContext &ctx)
{
    if ((ctx.buf[0] == 'c' || ctx.buf[0] == 'C')
     && (ctx.buf[1] == 's' || ctx.buf[1] == 'S')
     && (ctx.buf[2] == 'e' || ctx.buf[2] == 'E')
     && (ctx.buf[3] == 'q' || ctx.buf[3] == 'Q')
     && (!is_char_token(ctx.buf[4])))
    {
        return true;
    }

    return false;
}

inline void decode_token_cseq(DecodeContext &ctx)
{
    if ((ctx.buf[0] == 'c' || ctx.buf[0] == 'C')
     && (ctx.buf[1] == 's' || ctx.buf[1] == 'S')
     && (ctx.buf[2] == 'e' || ctx.buf[2] == 'E')
     && (ctx.buf[3] == 'q' || ctx.buf[3] == 'Q'))
    {
        ctx.buf += 4;
        ctx.len -= 4;
    }
    else
    {
        decode_return_fail(ctx, "token cseq err");
    }
}

inline bool verify_token_call_id(DecodeContext &ctx)
{
    if ((ctx.buf[0] == 'c' || ctx.buf[0] == 'C')
     && (ctx.buf[1] == 'a' || ctx.buf[1] == 'A')
     && (ctx.buf[2] == 'l' || ctx.buf[2] == 'L')
     && (ctx.buf[3] == 'l' || ctx.buf[3] == 'L')
     && (ctx.buf[4] == '-')
     && (ctx.buf[5] == 'i' || ctx.buf[5] == 'I')
     && (ctx.buf[6] == 'd' || ctx.buf[6] == 'D')
     && !is_char_token(ctx.buf[7]))
    {
        return true;
    }

    if ((ctx.buf[0] == 'I' || ctx.buf[0] == 'i')
     && (!is_char_token(ctx.buf[1])))
    {
        return true;
    }

    return false;
}

inline void decode_token_call_id(DecodeContext &ctx)
{
    if ((ctx.buf[0] == 'c' || ctx.buf[0] == 'C')
     && (ctx.buf[1] == 'a' || ctx.buf[1] == 'A')
     && (ctx.buf[2] == 'l' || ctx.buf[2] == 'L')
     && (ctx.buf[3] == 'l' || ctx.buf[3] == 'L')
     && (ctx.buf[4] == '-')
     && (ctx.buf[5] == 'i' || ctx.buf[5] == 'I')
     && (ctx.buf[6] == 'd' || ctx.buf[6] == 'D'))
    {
        ctx.buf += 7;
        ctx.len -= 7;
    }
    else if (ctx.buf[0] == 'i' || ctx.buf[0] == 'I')
    {
        ctx.buf += 1;
        ctx.len -= 1;
    }
    else
    {
        decode_return_fail(ctx, "token callid err");
    }
}

inline bool verify_token_from(DecodeContext &ctx)
{
    if ((ctx.buf[0] == 'f' || ctx.buf[0] == 'F')
     && (ctx.buf[1] == 'r' || ctx.buf[1] == 'R')
     && (ctx.buf[2] == 'o' || ctx.buf[2] == 'O')
     && (ctx.buf[3] == 'm' || ctx.buf[3] == 'M')
     && !is_char_token(ctx.buf[4]))
    {
        return true;
    }

    if ((ctx.buf[0] == 'f' || ctx.buf[0] == 'F')
     && !is_char_token(ctx.buf[1]))
    {
        return true;
    }

    return false;
}

inline void decode_token_from(DecodeContext &ctx)
{
    if ((ctx.buf[0] == 'f' || ctx.buf[0] == 'F')
     && (ctx.buf[1] == 'r' || ctx.buf[1] == 'R')
     && (ctx.buf[2] == 'o' || ctx.buf[2] == 'O')
     && (ctx.buf[3] == 'm' || ctx.buf[3] == 'M'))
    {
        ctx.buf += 4;
        ctx.len -= 4;
    }
    else if (ctx.buf[0] == 'f' || ctx.buf[0] == 'F')
    {
        ctx.buf += 1;
        ctx.len -= 1;
    }
    else
    {
        decode_return_fail(ctx, "token from err");
    }
}

inline bool verify_token_to(DecodeContext &ctx)
{
    if ((ctx.buf[0] == 't' || ctx.buf[0] == 'T')
     && (ctx.buf[1] == 'o' || ctx.buf[1] == 'O')
     && !is_char_token(ctx.buf[2]))
    {
        return true;
    }

    if ((ctx.buf[0] == 't' || ctx.buf[0] == 'T')
     && !is_char_token(ctx.buf[1]))
    {
        return true;
    }

    return false;
}

inline void decode_token_to(DecodeContext &ctx)
{
    if ((ctx.buf[0] == 't' || ctx.buf[0] == 'T')
     && (ctx.buf[1] == 'o' || ctx.buf[1] == 'O'))
    {
        ctx.buf += 2;
        ctx.len -= 2;
    }
    else if (ctx.buf[0] == 't' || ctx.buf[0] == 'T')
    {
        ctx.buf += 1;
        ctx.len -= 1;
    }
    else
    {
        decode_return_fail(ctx, "token to err");
    }
}

inline bool verify_token_via(DecodeContext &ctx)
{
    if ((ctx.buf[0] == 'v' || ctx.buf[0] == 'V')
     && (ctx.buf[1] == 'i' || ctx.buf[1] == 'I')
     && (ctx.buf[2] == 'a' || ctx.buf[2] == 'A')
     && !is_char_token(ctx.buf[3]))
    {
        return true;
    }

    if ((ctx.buf[0] == 'v' || ctx.buf[0] == 'V')
     && !is_char_token(ctx.buf[1]))
    {
        return true;
    }

    return false;
}

inline void decode_token_via(DecodeContext &ctx)
{
    if ((ctx.buf[0] == 'v' || ctx.buf[0] == 'V')
     && (ctx.buf[1] == 'i' || ctx.buf[1] == 'I')
     && (ctx.buf[2] == 'a' || ctx.buf[2] == 'A'))
    {
        ctx.buf += 3;
        ctx.len -= 3;
    }
    else if (ctx.buf[0] == 'v' || ctx.buf[0] == 'V')
    {
        ctx.buf += 1;
        ctx.len -= 1;
    }
    else
    {
        decode_return_fail(ctx, "token via err");
    }
}

inline bool verify_token_contact(DecodeContext &ctx)
{
    if ((ctx.buf[0] == 'c' || ctx.buf[0] == 'C')
     && (ctx.buf[1] == 'o' || ctx.buf[1] == 'O')
     && (ctx.buf[2] == 'n' || ctx.buf[2] == 'N')
     && (ctx.buf[3] == 't' || ctx.buf[3] == 'T')
     && (ctx.buf[4] == 'a' || ctx.buf[4] == 'A')
     && (ctx.buf[5] == 'c' || ctx.buf[5] == 'C')
     && (ctx.buf[6] == 't' || ctx.buf[6] == 'T')
     && !is_char_token(ctx.buf[7]))
    {
        return true;
    }

    if ((ctx.buf[0] == 'm' || ctx.buf[0] == 'M')
     && !is_char_token(ctx.buf[1]))
    {
        return true;
    }

    return false;
}

inline void decode_token_contact(DecodeContext &ctx)
{
    if ((ctx.buf[0] == 'c' || ctx.buf[0] == 'C')
     && (ctx.buf[1] == 'o' || ctx.buf[1] == 'O')
     && (ctx.buf[2] == 'n' || ctx.buf[2] == 'N')
     && (ctx.buf[3] == 't' || ctx.buf[3] == 'T')
     && (ctx.buf[4] == 'a' || ctx.buf[4] == 'A')
     && (ctx.buf[5] == 'c' || ctx.buf[5] == 'C')
     && (ctx.buf[6] == 't' || ctx.buf[6] == 'T'))
    {
        ctx.buf += 7;
        ctx.len -= 7;
    }
    else if (ctx.buf[0] == 'm' || ctx.buf[0] == 'M')
    {
        ctx.buf += 1;
        ctx.len -= 1;
    }
    else
    {
        decode_return_fail(ctx, "token contact err");
    }
}

inline bool verify_token_route(DecodeContext &ctx)
{
    if ((ctx.buf[0] == 'r' || ctx.buf[0] == 'R')
     && (ctx.buf[1] == 'o' || ctx.buf[1] == 'O')
     && (ctx.buf[2] == 'u' || ctx.buf[2] == 'U')
     && (ctx.buf[3] == 't' || ctx.buf[3] == 'T')
     && (ctx.buf[4] == 'e' || ctx.buf[4] == 'E')
     && !is_char_token(ctx.buf[5]))
    {
        return true;
    }

    return false;
}

inline void decode_token_route(DecodeContext &ctx)
{
    if ((ctx.buf[0] == 'r' || ctx.buf[0] == 'R')
     && (ctx.buf[1] == 'o' || ctx.buf[1] == 'O')
     && (ctx.buf[2] == 'u' || ctx.buf[2] == 'U')
     && (ctx.buf[3] == 't' || ctx.buf[3] == 'T')
     && (ctx.buf[4] == 'e' || ctx.buf[4] == 'E'))
    {
        ctx.buf += 5;
        ctx.len -= 5;
    }
    else
    {
        decode_return_fail(ctx, "token route err");
    }
}

inline bool verify_token_record_route(DecodeContext &ctx)
{
    if ((ctx.buf[0]  == 'r' || ctx.buf[0]  == 'R')
     && (ctx.buf[1]  == 'e' || ctx.buf[1]  == 'E')
     && (ctx.buf[2]  == 'c' || ctx.buf[2]  == 'C')
     && (ctx.buf[3]  == 'o' || ctx.buf[3]  == 'O')
     && (ctx.buf[4]  == 'r' || ctx.buf[4]  == 'R')
     && (ctx.buf[5]  == 'd' || ctx.buf[5]  == 'D')
     && (ctx.buf[6]  == '-')
     && (ctx.buf[7]  == 'r' || ctx.buf[7]  == 'R')
     && (ctx.buf[8]  == 'o' || ctx.buf[8]  == 'O')
     && (ctx.buf[9]  == 'u' || ctx.buf[9]  == 'U')
     && (ctx.buf[10] == 't' || ctx.buf[10] == 'T')
     && (ctx.buf[11] == 'e' || ctx.buf[11] == 'E')
     && !is_char_token(ctx.buf[12]))
    {
        return true;
    }

    return false;
}

inline void decode_token_record_route(DecodeContext &ctx)
{
    if ((ctx.buf[0]  == 'r' || ctx.buf[0]  == 'R')
     && (ctx.buf[1]  == 'e' || ctx.buf[1]  == 'E')
     && (ctx.buf[2]  == 'c' || ctx.buf[2]  == 'C')
     && (ctx.buf[3]  == 'o' || ctx.buf[3]  == 'O')
     && (ctx.buf[4]  == 'r' || ctx.buf[4]  == 'R')
     && (ctx.buf[5]  == 'd' || ctx.buf[5]  == 'D')
     && (ctx.buf[6]  == '-')
     && (ctx.buf[7]  == 'r' || ctx.buf[7]  == 'R')
     && (ctx.buf[8]  == 'o' || ctx.buf[8]  == 'O')
     && (ctx.buf[9]  == 'u' || ctx.buf[9]  == 'U')
     && (ctx.buf[10] == 't' || ctx.buf[10] == 'T')
     && (ctx.buf[11] == 'e' || ctx.buf[11] == 'E'))
    {
        ctx.buf += 12;
        ctx.len -= 12;
    }
    else
    {
        decode_return_fail(ctx, "token record-route err");
    }
}

// TokenStr = 1*token
void decode_str_token(DecodeContext &ctx, string &str)
{
    int i = 0;

    while (is_char_token(ctx.buf[i]))
        i += 1;

    if (i <= 0)
        decode_return_fail(ctx, "token str err");

    str.assign(ctx.buf, i);
    ctx.buf += i;
    ctx.len -= i;
}

// TokenStrList = *(tokenStr LWS)
// TokenStr = 1*token
void decode_str_token_list(DecodeContext &ctx, string &display)
{
    while (ctx.len > 0)
    {
        int i = 0;

        while (is_char_token(ctx.buf[i]) || is_char_wsp(ctx.buf[i]))
            i += 1;

        if (i > 0)
        {
            display.append(ctx.buf, i);
            ctx.buf += i;
            ctx.len -= i;
        }
        else
            break;

        if (!is_token_lws(&ctx.buf[0]))
            break;

        ctx.buf += 3;
        ctx.len -= 3;

        i = 0;

        while (is_char_wsp(ctx.buf[i]))
            i += 1;

        if (i > 0)
        {
            ctx.buf += i;
            ctx.len -= i;
        }

        //RFC3261: The line break and the whitespace at the beginning
        //  of the next line are treated as a single SP character.
        display.append(" ", 1);
    }
}

// QuoteStr = LDQUOTE *qtext RDQUOTE
void decode_str_quote(DecodeContext &ctx, string &str)
{
    int i = 0;

    decode_token_l_dquote(ctx);
    decode_check_fail(ctx);

    while (i < ctx.len)
    {
        if (ctx.buf[i] == '"')
            break;
        else if (ctx.buf[i] == '\\')
            i += 2;
        else
            i += 1;
    }

    str.assign(ctx.buf, i);
    ctx.buf += i;
    ctx.len -= i;

    decode_token_r_dquote(ctx);
}

void decode_str_param(DecodeContext &ctx, string &str)
{
    int i = 0;

    while (is_char_token(ctx.buf[i]) || is_char_host(ctx.buf[i]))
        i += 1;

    if (i <= 0)
        decode_return_fail(ctx, "param str err");

    str.assign(ctx.buf, i);
    ctx.buf += i;
    ctx.len -= i;
}

void decode_signed_int(DecodeContext &ctx, int32 &val)
{
    char *end;
    val = (int)strtol(ctx.buf, &end, 10);

    int len = end - ctx.buf;
    if (len <= 0)
        decode_return_fail(ctx, "signed int err");

    ctx.buf += len;
    ctx.len -= len;
}

void decode_unsigned_int(DecodeContext &ctx, uint32 &val)
{
    char *end;
    val = (u32)strtoul(ctx.buf, &end, 10);

    int len = end - ctx.buf;
    if (len <= 0)
        decode_return_fail(ctx, "unsigned int err");

    ctx.buf += len;
    ctx.len -= len;
}

// ParamVal = QuoteStr / TokenStr / HostStr
// QuoteStr = LDQUOTE *qtext RDQUOTE
void decode_param_value(DecodeContext &ctx, ParamValue &str)
{
    if (verify_token_l_dquote(ctx))
    {
        str.type = ParamValue::QUOTED;
        decode_str_quote(ctx, str.value);
    }
    else
    {
        str.type = ParamValue::TOKEN;
        decode_str_param(ctx, str.value);
    }
}

// Param = ParamName [EQUAL ParamValue]
void decode_param(DecodeContext &ctx, std::pair<string, ParamValue> &param)
{
    decode_str_token(ctx, param.first);
    decode_check_fail(ctx);

    if (verify_token_delimiter(ctx, '='))
    {
		decode_token_delimiter(ctx, '=');
        decode_check_fail(ctx);

        decode_param_value(ctx, param.second);
    }
}

// Method = ... / Extension
void decode_method(DecodeContext &ctx, Method &method)
{
    if (verify_token_invite(ctx))
    {
        method = METHOD_INVITE;
        ctx.buf += 6;
        ctx.len -= 6;
    }
    else if (verify_token_ack(ctx))
    {
        method = METHOD_ACK;
        ctx.buf += 3;
        ctx.len -= 3;
    }
    else if (verify_token_bye(ctx))
    {
        method = METHOD_BYE;
        ctx.buf += 3;
        ctx.len -= 3;
    }
    else if (verify_token_cancel(ctx))
    {
        method = METHOD_CANCEL;
        ctx.buf += 6;
        ctx.len -= 6;
    }
    else if (verify_token_update(ctx))
    {
        method = METHOD_UPDATE;
        ctx.buf += 6;
        ctx.len -= 6;
    }
    else if (verify_token_prack(ctx))
    {
        method = METHOD_PRACK;
        ctx.buf += 5;
        ctx.len -= 5;
    }
    else if (verify_token_message(ctx))
    {
        method = METHOD_MESSAGE;
        ctx.buf += 7;
        ctx.len -= 7;
    }
    else if (verify_token_info(ctx))
    {
        method = METHOD_INFO;
        ctx.buf += 4;
        ctx.len -= 4;
    }
    else if (verify_token_register(ctx))
    {
        method = METHOD_REGISTER;
        ctx.buf += 8;
        ctx.len -= 8;
    }
    else if (verify_token_options(ctx))
    {
        method = METHOD_OPTIONS;
        ctx.buf += 7;
        ctx.len -= 7;
    }
    else if (verify_token_subscribe(ctx))
    {
        method = METHOD_SUBSCRIBE;
        ctx.buf += 9;
        ctx.len -= 9;
    }
    else if (verify_token_notify(ctx))
    {
        method = METHOD_NOTIFY;
        ctx.buf += 6;
        ctx.len -= 6;
    }
    else if (verify_token_refer(ctx))
    {
        method = METHOD_REFER;
        ctx.buf += 5;
        ctx.len -= 5;
    }
    else if (verify_token_publish(ctx))
    {
        method = METHOD_PUBLISH;
        ctx.buf += 7;
        ctx.len -= 7;
    }
    else
    {
        decode_return_fail(ctx, "unknown method");
    }
}

void decode_scheme(DecodeContext &ctx, Scheme &scheme)
{
    if ((ctx.buf[0] == 's' || ctx.buf[0] == 'S')
     && (ctx.buf[1] == 'i' || ctx.buf[1] == 'I')
     && (ctx.buf[2] == 'p' || ctx.buf[2] == 'P')
     && !is_char_scheme(ctx.buf[3]))
    {
        scheme = Scheme::SCHEME_SIP;
        ctx.buf += 3;
        ctx.len -= 3;
    }
    else
    if ((ctx.buf[0] == 's' || ctx.buf[0] == 'S')
     && (ctx.buf[1] == 'i' || ctx.buf[1] == 'I')
     && (ctx.buf[2] == 'p' || ctx.buf[2] == 'P')
     && (ctx.buf[3] == 's' || ctx.buf[3] == 'S')
     && !is_char_scheme(ctx.buf[4]))
    {
        scheme = Scheme::SCHEME_SIPS;
        ctx.buf += 4;
        ctx.len -= 4;
    }
    else
    {
        decode_return_fail(ctx, "unknown uri scheme");
    }
}

// UserInfo = User [":" Pswd] "@"
bool verify_user_info(DecodeContext &ctx)
{
    int i = 0;

    while (is_char_user(ctx.buf[i]))
        i += 1;

    if (ctx.buf[i] == ':')
    {
        i += 1;
        while (is_char_passwd(ctx.buf[i]))
            i += 1;
    }

    if (ctx.buf[i] == '@')
        return true;

    return false;
}

void decode_uri_user(DecodeContext &ctx, string &user)
{
    int i = 0;

    while (is_char_user(ctx.buf[i]))
        i += 1;

    if (i <= 0)
        decode_return_fail(ctx, "dec uri user err");

    user.assign(ctx.buf, i);
    ctx.buf += i;
    ctx.len -= i;
}

void decode_uri_passwd(DecodeContext &ctx, string &passwd)
{
    int i = 0;

    while (is_char_passwd(ctx.buf[i]))
        i += 1;

    passwd.assign(ctx.buf, i);
    ctx.buf += i;
    ctx.len -= i;
}

// HostName = *(label ".") toplabel ["."]
bool verify_host_name(DecodeContext &ctx)
{
    int i = 0;

    while (i < ctx.len)
    {
        if (isalpha(ctx.buf[i]) || ctx.buf[i] == '-')
            return true;
        else if (isdigit(ctx.buf[i]) || ctx.buf[i] == '.')
            i += 1;
        else
            return false;
    }

    return false;
}

// HostName = *(label ".") toplabel ["."]
// label = alphanum / alphanum *( alphanum / "-" ) alphanum
// toplabel = ALPHA / ALPHA *( alphanum / "-" ) alphanum
void decode_host_name(DecodeContext &ctx, string &name)
{
    int i = 0;

    while (isalnum(ctx.buf[i]) || ctx.buf[i] == '-' || ctx.buf[i] == '.')
        i += 1;

    if (i <= 0)
        decode_return_fail(ctx, "dec host name err");

    name.assign(ctx.buf, i);
    ctx.buf += i;
    ctx.len -= i;
}

// In4Addr = 1*3DIGIT "." 1*3DIGIT "." 1*3DIGIT "." 1*3DIGIT
void decode_in4_addr(DecodeContext &ctx, string &addr)
{
    int i = 0;

    while (isdigit(ctx.buf[i]) || ctx.buf[i] == '.')
        i += 1;

    if (i <= 0)
        decode_return_fail(ctx, "dec in4 addr err");

    addr.assign(ctx.buf, i);
    ctx.buf += i;
    ctx.len -= i;
}

// In6Addr = "[" hexpart [ ":" In4Addr ] "]"
// hexpart	= hexseq / hexseq "::" [ hexseq ] / "::" [ hexseq ]
// hexseq = 1*4HEXDIG *( ":" 1*4HEXDIG )
void decode_in6_addr(DecodeContext &ctx, string &addr)
{
    if (ctx.buf[0] != '[')
        decode_return_fail(ctx, "dec in6 addr err");

    ctx.buf += 1;
    ctx.len -= 1;

    int i = 0;

    while (isdigit(ctx.buf[i]) || ctx.buf[i] == ':' || ctx.buf[i] == '.')
        i += 1;

    if (i <= 0)
        decode_return_fail(ctx, "dec in6 addr err");

    addr.assign(ctx.buf, i);
    ctx.buf += i;
    ctx.len -= i;

    if (ctx.buf[0] != ']')
        decode_return_fail(ctx, "dec in6 addr err");

    ctx.buf += 1;
    ctx.len -= 1;
}

// Host = HostName / In4Addr / In6Addr
void decode_host(DecodeContext &ctx, Host &host)
{
    if (ctx.buf[0] != '[')
    {
        if (verify_host_name(ctx))
        {
            host.type = Host::HOST_NAME;
            decode_host_name(ctx, host.value);
        }
        else
        {
            host.type = Host::IN4_ADDR;
            decode_in4_addr(ctx, host.value);
        }
    }
    else
    {
        host.type = Host::IN6_ADDR;
        decode_in6_addr(ctx, host.value);
    }
}

void decode_uri_parm_str(DecodeContext &ctx, string &str)
{
    int i = 0;

    while (is_char_uri_param(ctx.buf[i]))
        i += 1;

    if (i <= 0)
        decode_return_fail(ctx, "dec uri parm str err");

    str.assign(ctx.buf, i);
    ctx.buf += i;
    ctx.len -= i;
}

// UriParm = ParmName ["=" ParmValue]
void decode_uri_parm(DecodeContext &ctx, std::pair<string, string> &param)
{
    decode_uri_parm_str(ctx, param.first);
    decode_check_fail(ctx);

    if (ctx.buf[0] == '=')
    {
        ctx.buf += 1;
        ctx.len -= 1;

        decode_uri_parm_str(ctx, param.second);
    }
}

void decode_uri_header_str(DecodeContext &ctx, string &str)
{
    int i = 0;

    while (is_char_uri_header(ctx.buf[i]))
        i += 1;

    if (i <= 0)
        decode_return_fail(ctx, "dec uri hdr str err");

    str.assign(ctx.buf, i);
    ctx.buf += i;
    ctx.len -= i;
}

// UriHeader = HeaderName "=" HeaderValue
void decode_uri_header(DecodeContext &ctx, std::pair<string, string> &header)
{
    decode_uri_header_str(ctx, header.first);
    decode_check_fail(ctx);

    if (ctx.buf[0] != '=')
        decode_return_fail(ctx, "dec uri hdr err");

    ctx.buf += 1;
    ctx.len -= 1;

    decode_uri_header_str(ctx, header.second);
}

bool verify_uri_spec(DecodeContext &ctx)
{
    int i = 0;

    if (isalpha(ctx.buf[i]))
        i += 1;
    else
        return false;

    while (is_char_scheme(ctx.buf[i]))
        i += 1;

    if (ctx.buf[i] != ':')
        return false;

    return true;
}

// Uri = SipUri / SipsUri / TelUri
// SipUri = Scheme ":" [Userinfo] Host [":" Port] *(";" UriParam) ["?" UriHeader *("&" UriHeader)]
// Scheme = "sip:" / "sips:"
// UserInfo = User [":" Pswd] "@"
void decode_uri_spec(DecodeContext &ctx, UriSpec &uri)
{
    decode_scheme(ctx, uri.scheme);
    decode_check_fail(ctx);

    if (ctx.buf[0] != ':')
        decode_return_fail(ctx, "dec delimiter : err");

    ctx.buf += 1;
    ctx.len -= 1;

    if (verify_user_info(ctx))
    {
        decode_uri_user(ctx, uri.user);
        decode_check_fail(ctx);

        if (ctx.buf[0] == ':')
        {
            ctx.buf += 1;
            ctx.len -= 1;

            decode_uri_passwd(ctx, uri.passwd);
            decode_check_fail(ctx);
        }

        if (ctx.buf[0] != '@')
            decode_return_fail(ctx, "dec uri info err");

        ctx.buf += 1;
        ctx.len -= 1;
    }

    decode_host(ctx, uri.host);
    decode_check_fail(ctx);

	uri.port = 0;

    if (ctx.buf[0] == ':')
    {
        ctx.buf += 1;
        ctx.len -= 1;

        decode_unsigned_int(ctx, uri.port);
        decode_check_fail(ctx);
    }

    while (ctx.buf[0] == ';')
    {
        ctx.buf += 1;
        ctx.len -= 1;

        std::pair<string, string> param;
        decode_uri_parm(ctx, param);
        decode_check_fail(ctx);
        uri.params.insert(std::move(param));
    }

    if (ctx.buf[0] == '?')
    {
        ctx.buf += 1;
        ctx.len -= 1;

        std::pair<string, string> header;
        decode_uri_header(ctx, header);
        decode_check_fail(ctx);
        uri.headers.insert(std::move(header));

        while (ctx.buf[0] == '&')
        {
            ctx.buf += 1;
            ctx.len -= 1;

            decode_uri_header(ctx, header);
            decode_check_fail(ctx);
            uri.headers.insert(std::move(header));
        }
    }
}

bool verify_display_name(DecodeContext &ctx)
{
    if (verify_token_l_dquote(ctx))
        return true;

    if (is_char_token(ctx.buf[0]))
        return true;

    return false;
}

// DispName = QuoteStr / TokenStrList
void decode_display_name(DecodeContext &ctx, string &display)
{
    if (verify_token_l_dquote(ctx))
        decode_str_quote(ctx, display);
    else
        decode_str_token_list(ctx, display);
}

// CSeq = "CSeq" HCOLON 1*DIGIT LWS Method CRLF
void decode_header_cseq(DecodeContext &ctx, HeaderCseq &cseq)
{
    decode_token_cseq(ctx);
    decode_check_fail(ctx);

    decode_token_hcolon(ctx);
    decode_check_fail(ctx);

    decode_unsigned_int(ctx, cseq.seq_num);
    decode_check_fail(ctx);

    decode_token_lws(ctx);
    decode_check_fail(ctx);

    decode_method(ctx, cseq.method);
    decode_check_fail(ctx);

    decode_token_crlf(ctx);
}

// CallId  = ("call-id"/"i") HCOLON word ["@" word] CRLF
void decode_header_call_id(DecodeContext &ctx, string &call_id)
{
    decode_token_call_id(ctx);
    decode_check_fail(ctx);

    decode_token_hcolon(ctx);
    decode_check_fail(ctx);

    int i = 0;

    while (is_char_word(ctx.buf[i]))
        i += 1;

    if (ctx.buf[i] == '@')
    {
        i += 1;
		while (is_char_word(ctx.buf[i]))
			i += 1;
    }

    if (i <= 0)
        decode_return_fail(ctx, "dec hdr callid err");

    call_id.assign(ctx.buf, i);
    ctx.buf += i;
    ctx.len -= i;

    decode_token_crlf(ctx);
}

// From = ("From"/"f") HCOLON (Uri / [DispName] LANGLE Uri RANGLE) *(SEMI FromParam) CRLF
void decode_header_from(DecodeContext &ctx, HeaderFrom &from)
{
    decode_token_from(ctx);
    decode_check_fail(ctx);

    decode_token_hcolon(ctx);
    decode_check_fail(ctx);

    if (verify_uri_spec(ctx))
    {
        decode_uri_spec(ctx, from.uri);
        decode_check_fail(ctx);
    }
    else
    {
        if (verify_display_name(ctx))
        {
            decode_display_name(ctx, from.display);
            decode_check_fail(ctx);
        }

        decode_token_l_angle(ctx);
        decode_check_fail(ctx);

        decode_uri_spec(ctx, from.uri);
        decode_check_fail(ctx);

        decode_token_r_angle(ctx);
        decode_check_fail(ctx);
    }

    while (verify_token_delimiter(ctx, ';'))
    {
        decode_token_delimiter(ctx, ';');
        decode_check_fail(ctx);

        std::pair<string, ParamValue> param;

        decode_param(ctx, param);
        decode_check_fail(ctx);

        from.params.insert(std::move(param));
    }

    decode_token_crlf(ctx);
}

// To = ("To"/"t") HCOLON (Uri / [DispName] LANGLE Uri RANGLE) *(SEMI ToParam) CRLF
void decode_header_to(DecodeContext &ctx, HeaderTo &to)
{
    decode_token_to(ctx);
    decode_check_fail(ctx);

    decode_token_hcolon(ctx);
    decode_check_fail(ctx);

    if (verify_uri_spec(ctx))
    {
        decode_uri_spec(ctx, to.uri);
        decode_check_fail(ctx);
    }
    else
    {
        if (verify_display_name(ctx))
        {
            decode_display_name(ctx, to.display);
            decode_check_fail(ctx);
        }

        decode_token_l_angle(ctx);
        decode_check_fail(ctx);

        decode_uri_spec(ctx, to.uri);
        decode_check_fail(ctx);

        decode_token_r_angle(ctx);
        decode_check_fail(ctx);
    }

    while (verify_token_delimiter(ctx, ';'))
    {
        decode_token_delimiter(ctx, ';');
        decode_check_fail(ctx);

        std::pair<string, ParamValue> param;

        decode_param(ctx, param);
        decode_check_fail(ctx);

        to.params.insert(std::move(param));
    }

    decode_token_crlf(ctx);
}

// ViaItem = "SIP" SLASH "2.0" SLASH Proto LWS Host [COLON Port] *(SEMI ViaParam)
void decode_via_item(DecodeContext &ctx, HeaderVia &via)
{
    decode_token_sip(ctx);
    decode_check_fail(ctx);

    decode_token_delimiter(ctx, '/');
    decode_check_fail(ctx);

    decode_token_ver(ctx);
    decode_check_fail(ctx);

    decode_token_delimiter(ctx, '/');
    decode_check_fail(ctx);

    decode_str_token(ctx, via.proto);
    decode_check_fail(ctx);

    decode_token_lws(ctx);
    decode_check_fail(ctx);

    decode_host(ctx, via.host);
    decode_check_fail(ctx);

	via.port = 0;

    if (verify_token_delimiter(ctx, ':'))
    {
        decode_token_delimiter(ctx, ':');
        decode_check_fail(ctx);

        decode_unsigned_int(ctx, via.port);
        decode_check_fail(ctx);
    }

    while (verify_token_delimiter(ctx, ';'))
    {
        decode_token_delimiter(ctx, ';');
        decode_check_fail(ctx);

        std::pair<string, ParamValue> param;
        decode_param(ctx, param);
        decode_check_fail(ctx);
        via.params.insert(std::move(param));
    }
}

// Via = ("Via"/"v") HCOLON ViaItem *(COMMA ViaItem) CRLF
void decode_header_vias(DecodeContext &ctx, std::list<HeaderVia> &vias)
{
    decode_token_via(ctx);
    decode_check_fail(ctx);

    decode_token_hcolon(ctx);
    decode_check_fail(ctx);

    HeaderVia via;
    decode_via_item(ctx, via);
    decode_check_fail(ctx);
    vias.push_back(std::move(via));

    while (verify_token_delimiter(ctx, ','))
    {
        decode_token_delimiter(ctx, ',');
        decode_check_fail(ctx);

        decode_via_item(ctx, via);
        decode_check_fail(ctx);
        vias.push_back(std::move(via));
    }

    decode_token_crlf(ctx);
}

// RouteItem = [DispName] LANGLE Uri RANGLE *(SEMI Param)
void decode_route_item(DecodeContext &ctx, HeaderRoute &route)
{
    if (verify_display_name(ctx))
    {
        decode_display_name(ctx, route.display);
        decode_check_fail(ctx);
    }

    decode_token_l_angle(ctx);
    decode_check_fail(ctx);

    decode_uri_spec(ctx, route.uri);
    decode_check_fail(ctx);

    decode_token_r_angle(ctx);
    decode_check_fail(ctx);

    while (verify_token_delimiter(ctx, ';'))
    {
        std::pair<string, ParamValue> param;
        decode_param(ctx, param);
        decode_check_fail(ctx);
        route.params.insert(std::move(param));
    }
}

// Route = "Route" HCOLON RouteItem *(COMMA RouteItem) CRLF
void decode_header_routes(DecodeContext &ctx, std::list<HeaderRoute> &routes)
{
    decode_token_route(ctx);
    decode_check_fail(ctx);

    decode_token_hcolon(ctx);
    decode_check_fail(ctx);

    HeaderRoute route;
    decode_route_item(ctx, route);
    decode_check_fail(ctx);
    routes.push_back(std::move(route));

    while (verify_token_delimiter(ctx, ','))
    {
        decode_token_delimiter(ctx, ',');
        decode_check_fail(ctx);

        decode_route_item(ctx, route);
        decode_check_fail(ctx);
        routes.push_back(std::move(route));
    }

    decode_token_crlf(ctx);
}

// RecordRoute = "Record-Route" HCOLON RouteItem *(COMMA RouteItem) CRLF
void decode_header_record_routes(DecodeContext &ctx, std::list<HeaderRoute> &record_routes)
{
    decode_token_record_route(ctx);
    decode_check_fail(ctx);

    decode_token_hcolon(ctx);
    decode_check_fail(ctx);

    HeaderRoute route;
    decode_route_item(ctx, route);
    decode_check_fail(ctx);
    record_routes.push_back(std::move(route));

    while (verify_token_delimiter(ctx, ','))
    {
        decode_token_delimiter(ctx, ',');
        decode_check_fail(ctx);

        decode_route_item(ctx, route);
        decode_check_fail(ctx);
        record_routes.push_back(std::move(route));
    }

    decode_token_crlf(ctx);
}

// ContactItem = (Uri / [DispName] LANGLE Uri RANGLE) *(SEMI ContactParam)
void decode_contact_item(DecodeContext &ctx, HeaderContact &contact)
{
    if (verify_uri_spec(ctx))
    {
        decode_uri_spec(ctx, contact.uri);
        decode_check_fail(ctx);
    }
    else
    {
        if (verify_display_name(ctx))
        {
            decode_display_name(ctx, contact.display);
            decode_check_fail(ctx);
        }

        decode_token_l_angle(ctx);
        decode_check_fail(ctx);

        decode_uri_spec(ctx, contact.uri);
        decode_check_fail(ctx);

        decode_token_r_angle(ctx);
        decode_check_fail(ctx);
    }

    while (verify_token_delimiter(ctx, ';'))
    {
        decode_token_delimiter(ctx, ';');
        decode_check_fail(ctx);

        std::pair<string, ParamValue> param;
        decode_param(ctx, param);
        decode_check_fail(ctx);
        contact.params.insert(std::move(param));
    }
}

// Contact = ("Contact"/"m") HCOLON (STAR / ContactItem *(COMMA ContactItem)) CRLF
void decode_header_contacts(DecodeContext &ctx, std::list<HeaderContact> &contacts)
{
    decode_token_contact(ctx);
    decode_check_fail(ctx);

    decode_token_hcolon(ctx);
    decode_check_fail(ctx);

    if (verify_token_delimiter(ctx, '*'))
    {
        decode_token_delimiter(ctx, '*');
        decode_check_fail(ctx);

        HeaderContact contact;
        contact.type = HeaderContact::STAR;
        contacts.push_back(std::move(contact));
    }
    else
    {
        HeaderContact contact;
        contact.type = HeaderContact::SPEC;

        decode_contact_item(ctx, contact);
        decode_check_fail(ctx);
        contacts.push_back(std::move(contact));

        while (verify_token_delimiter(ctx, ','))
        {
            decode_token_delimiter(ctx, ',');
            decode_check_fail(ctx);

            decode_contact_item(ctx, contact);
            decode_check_fail(ctx);
            contacts.push_back(std::move(contact));
        }
    }

    decode_token_crlf(ctx);
}

// ExtValue = *(Graph / UTF8C / LWS)
void decode_ext_value(DecodeContext &ctx, string &val)
{
    while (ctx.len > 0)
    {
        int i = 0;

        while (isgraph(ctx.buf[i]) || is_char_wsp(ctx.buf[i]) || ctx.buf[i] < 0)
            i += 1;

        if (i > 0)
        {
            val.append(ctx.buf, i);
            ctx.buf += i;
            ctx.len -= i;
        }

        if (!is_token_lws(&ctx.buf[0]))
            break;

        ctx.buf += 3;
        ctx.len -= 3;

        i = 0;

        while (is_char_wsp(ctx.buf[i]))
            i += 1;

        if (i > 0)
        {
            ctx.buf += i;
            ctx.len -= i;
        }

        //RFC3261: The line break and the whitespace at the beginning
        //  of the next line are treated as a single SP character.
        val.append(" ", 1);
    }
}

// Extension = ExtName HCOLON ExtValue CRLF
void decode_header_ext(DecodeContext &ctx, HeaderExt &ext)
{
    decode_str_token(ctx, ext.name);
    decode_check_fail(ctx);

    decode_token_hcolon(ctx);
    decode_check_fail(ctx);

    decode_ext_value(ctx, ext.value);
    decode_check_fail(ctx);

    decode_token_crlf(ctx);
}

// Headers = *(... / Extension)
void decode_headers(DecodeContext &ctx, Headers &headers)
{
    while (ctx.buf[0])
    {
        if (verify_token_cseq(ctx))
        {
            HeaderCseq cseq;
            decode_header_cseq(ctx, cseq);
            decode_check_fail(ctx);
            headers.cseq = std::move(cseq);
        }
        else if (verify_token_call_id(ctx))
        {
            string call_id;
            decode_header_call_id(ctx, call_id);
            decode_check_fail(ctx);
            headers.call_id = std::move(call_id);
        }
        else if (verify_token_from(ctx))
        {
            HeaderFrom from;
            decode_header_from(ctx, from);
            decode_check_fail(ctx);
            headers.from = std::move(from);
        }
        else if (verify_token_to(ctx))
        {
            HeaderTo to;
            decode_header_to(ctx, to);
            decode_check_fail(ctx);
            headers.to = std::move(to);
        }
        else if (verify_token_via(ctx))
        {
            decode_header_vias(ctx, headers.vias);
            decode_check_fail(ctx);
        }
        else if (verify_token_route(ctx))
        {
            decode_header_routes(ctx, headers.routes);
            decode_check_fail(ctx);
        }
        else if (verify_token_record_route(ctx))
        {
            decode_header_record_routes(ctx, headers.record_routes);
            decode_check_fail(ctx);
        }
        else if (verify_token_contact(ctx))
        {
            decode_header_contacts(ctx, headers.contacts);
            decode_check_fail(ctx);
        }
        else if (is_char_token(ctx.buf[0]))
        {
            HeaderExt ext;
            decode_header_ext(ctx, ext);
            decode_check_fail(ctx);
            headers.exts.push_back(std::move(ext));
        }
        else
        {
            return;
        }
    }
}

// Request = RequestLine Headers CRLF [Body]
// RequestLine = Method SP Uri SP Version CRLF
void decode_request(DecodeContext &ctx, Request &req)
{
    decode_method(ctx, req.method);
    decode_check_fail(ctx);

    decode_token_space(ctx);
    decode_check_fail(ctx);

    decode_uri_spec(ctx, req.req_uri);
    decode_check_fail(ctx);

    decode_token_space(ctx);
    decode_check_fail(ctx);

    decode_token_sip_ver(ctx);
    decode_check_fail(ctx);

    decode_token_crlf(ctx);
    decode_check_fail(ctx);

    decode_headers(ctx, req.headers);
    decode_check_fail(ctx);

    decode_token_crlf(ctx);
    decode_check_fail(ctx);

    if (ctx.len > 0)
    {
        req.msg_body.assign(ctx.buf, ctx.len);
        ctx.buf = NULL;
        ctx.len = 0;
    }
}

// ReasonPhrase = *(Phrase / Escaped / UTF8C)
void decode_reason_phrase(DecodeContext &ctx, string &reason)
{
    int i = 0;

    while (isgraph(ctx.buf[i]) || is_char_wsp(ctx.buf[i]) || ctx.buf[i] < 0)
        i += 1;

    if (i > 0)
    {
        reason.assign(ctx.buf, i);
        ctx.buf += i;
        ctx.len -= i;
    }
}

// Response = StatusLine Headers CRLF [Body]
// StatusLine = Version SP StatusCode SP ReasonPhrase CRLF
void decode_response(DecodeContext &ctx, Response &rsp)
{
    decode_token_sip_ver(ctx);
    decode_check_fail(ctx);

    decode_token_space(ctx);
    decode_check_fail(ctx);

    if (!isdigit(ctx.buf[0]) || !isdigit(ctx.buf[1]) || !isdigit(ctx.buf[2]))
        decode_return_fail(ctx, "dec status code err");

    rsp.status_code = (ctx.buf[0] - '0') * 100 + (ctx.buf[1] - '0') * 10 + (ctx.buf[2] - '0');
    ctx.buf += 3;
    ctx.len -= 3;

    decode_token_space(ctx);
    decode_check_fail(ctx);

    decode_reason_phrase(ctx, rsp.reason);
    decode_check_fail(ctx);

    decode_token_crlf(ctx);
    decode_check_fail(ctx);

    decode_headers(ctx, rsp.headers);
    decode_check_fail(ctx);

    decode_token_crlf(ctx);
    decode_check_fail(ctx);

    if (ctx.len > 0)
    {
        rsp.msg_body.assign(ctx.buf, ctx.len);
        ctx.buf = NULL;
        ctx.len = 0;
    }
}

inline bool verify_request(DecodeContext &ctx)
{
    if ((ctx.buf[0] == 'S' || ctx.buf[0] == 's')
     && (ctx.buf[1] == 'I' || ctx.buf[1] == 'i')
     && (ctx.buf[2] == 'P' || ctx.buf[2] == 'p')
     && (ctx.buf[3] == '/'))
    {
        return false;
    }

    return true;
}

// Message = *(CRCF) (Request / Response)
int decode_message(const char *buf, int len, Message &msg)
{
    DecodeContext ctx;
    ctx.buf = buf;
    ctx.len = len;
    ctx.ret = 0;

    //RFC3261: Implementations processing SIP messages over stream-oriented
    //  transports MUST ignore any CRLF appearing before the start-line [H4.1].
    for (int i=0; i<ctx.len; ++i)
    {
        if (ctx.buf[i] != '\r' && ctx.buf[i] != '\n')
        {
            ctx.buf += i;
            ctx.len -= i;
            break;
        }
    }

    if (verify_request(ctx))
    {
        msg.type = Message::REQUEST;
        decode_request(ctx, msg.req);
    }
    else
    {
        msg.type = Message::RESPONSE;
        decode_response(ctx, msg.rsp);
    }

	if (ctx.ret)
		printf("err: %s\n", ctx.err.c_str());

    return ctx.ret;
}

int Message::from_str(const string &input)
{
    return decode_message(input.c_str(), input.length(), *this);
}

int Message::from_str(const strref &input)
{
    return decode_message(input.c_str(), input.length(), *this);
}

int Message::from_str(const char *input, int len)
{
    return decode_message(input, len, *this);
}

}
